﻿///<reference path="../jquery-1.4.4.min.js" />

/*
*
*   formValid
*
*/

/*
* $('#target').formValid({
selector: 'selector',
*     errorMessage: 'Error message' // some text or a function return the text.
* });
*/

(function ($) {

    /*
    * helpers
    */
    var isNumber = function (v) {
        return typeof v === 'number' && isFinite(v);
    };

    var isAncestor = function (p, c) {
        var ret = false;
        if (p && c) {
            if (p.contains) {
                return p.contains(c);
            } else if (p.compareDocumentPosition) {
                return !!(p.compareDocumentPosition(c) & 16);
            } else {
                while (c = c.parentNode) {
                    ret = c == p || ret;
                }
            }
        }
        return ret;
    };

    var getElementPosition = function (element) {
        var result = { x: 0, y: 0, width: 0, height: 0 };
        if (element.offsetParent) {
            result.x = element.offsetLeft;
            result.y = element.offsetTop;
            var parent = element.offsetParent;
            while (parent) {
                result.x += parent.offsetLeft;
                result.y += parent.offsetTop;
                var parentTagName = parent.tagName.toLowerCase();
                if (parentTagName != "table" &&
                    parentTagName != "body" &&
                    parentTagName != "html" &&
                    parentTagName != "div" &&
                    parent.clientTop &&
                    parent.clientLeft) {
                    result.x += parent.clientLeft;
                    result.y += parent.clientTop;
                }
                parent = parent.offsetParent;
            }
        }
        else if (element.left && element.top) {
            result.x = element.left;
            result.y = element.top;
        }
        else {
            if (element.x) {
                result.x = element.x;
            }
            if (element.y) {
                result.y = element.y;
            }
        }
        if (element.offsetWidth && element.offsetHeight) {
            result.width = element.offsetWidth;
            result.height = element.offsetHeight;
        }
        else if (element.style && element.style.pixelWidth && element.style.pixelHeight) {
            result.width = element.style.pixelWidth;
            result.height = element.style.pixelHeight;
        }
        return result;
    };

    /*
    * component class
    */
    $.formValid = function (options, refObj) {
        this.refObj = refObj;
        this.settings = $.extend({}, $.formValid.defaults, options);
        this.settings.lazyFunc = [];
        if (this.settings.lazyInitialize === false) { this.initialize(); }
    };

    $.extend($.formValid, {

        /*
        * default configuration
        * change it as needed.
        */
        defaults: {
            debug: false,
            width: 'auto',
            showed: false,
            showArrow: true,
            resizeTimeout: false,
            lazyInitialize: true,
            arrowWidth: 20,
            arrowHeight: 16,
            offsetX: -30, // number value or 'center'
            offsetY: 10,
            renderTo: '.form-valid-cache',
            onShow: null,
            onHide: null
        },

        prototype: {

            el: null,

            settings: null,

            refObj: null, arrowObj: null,

            // private
            initialize: function () {
                // valid elements
                if (this.settings.inited === true) { return; }
                this.settings.inited = true;
                var html = this.buildHtml();
                this.el = $(html).insertAfter(this.refObj);
                if (this.settings.width == 'auto') {
                    this.el.css('min-width', 50);
                } else {
                    this.el.width(this.settings.width);
                }
                this.arrowObj = $('.arrow', this.el);
                if (!this.settings.showArrow) {
                    this.arrowObj.remove();
                    this.arrowObj = null;
                }

                // lazy func
                var funcs = this.settings.lazyFunc;
                delete this.settings['lazyFunc'];
                for (var i = 0; i < funcs.length; i++)
                    funcs[i].call(this);

                // event handlers
                var self = this, timeoutId;
                //                this.settings._hide = function (ev) {
                //                    if (!isAncestor(self.el.get(0), ev.target)) {
                //                        self.hide();
                //                    }
                //                };
                this.settings._reisze = self.settings.resizeTimeout ? function () {
                    window.clearTimeout(timeoutId);
                    timeoutId = window.setTimeout(function () {
                        self.fixPos();
                    }, 50);
                } : function () {
                    self.fixPos();
                };
            },

            // private
            buildHtml: function () {
                var html = [];
                html.push('<span class="field-validation-error"></span>');
                //                html.push('<div class="form-valid">');
                //                html.push('<div class="arrow"></div>');
                //                html.push('<div class="content">');
                //                html.push('</div>');
                //                html.push('</div>');
                return html.join('');
            },

            // private
            getPos: function (refInfo) {
                var winHeight = $(window).height(), winWidth = $(window).width();
                var scrollTop = $(window).scrollTop(), scrollLeft = $(window).scrollLeft();
                var selHeight = this.el.outerHeight(), selWidth = this.el.outerWidth();
                if (this.settings.offsetX == 'center') { this.settings.offsetX = -(selWidth / 2) + this.settings.arrowWidth / 2; }
                if (this.settings.offsetY == 'middle') { this.settings.offsetY = -(selHeight / 2) + this.settings.arrowHeight / 2 }
                var left = 0, top = 0, arrowLeft = null;
                if (refInfo.y - scrollTop - selHeight + this.settings.offsetY > 0) {
                    top = refInfo.y - selHeight + this.settings.offsetY;
                    this.arrowTop(false);
                } else {
                    top = refInfo.y + refInfo.height + this.settings.offsetY;
                    this.arrowTop(true);
                }
                if (winWidth - refInfo.x - scrollLeft - selWidth + this.settings.offsetX > 0) {
                    left = refInfo.x + refInfo.width + this.settings.offsetX;
                    if (refInfo.width < selWidth) {
                        arrowLeft = selWidth - refInfo.width / 2 - this.settings.arrowWidth / 2 + this.settings.offsetX;
                    }
                } else {
                    left = refInfo.x + refInfo.width + this.settings.offsetX;
                    if (refInfo.width < selWidth) {
                        arrowLeft = refInfo.width / 2 - this.settings.arrowWidth / 2 - this.settings.offsetX;
                    }
                }
                return {
                    left: left,
                    top: top,
                    arrowLeft: arrowLeft
                };
            },

            // public
            arrowTop: function (upTop) {
                if (!this.settings.showArrow) { return; }
                upTop = (upTop !== false);
                if (upTop) {
                    this.arrowObj.insertBefore($('.content', this.el));
                    this.arrowObj.removeClass('down');
                } else {
                    this.arrowObj.insertAfter($('.content', this.el));
                    this.arrowObj.addClass('down');
                }
            },

            // public
            arrowLeft: function (offset) {
                if (this.settings.inited !== true) { return; }
                if (!this.settings.showArrow) { return; }
                var max = this.el.width() - this.settings.arrowWidth;
                if (isNumber(offset)) {
                    offset = Math.max(offset, 0);
                    offset = Math.min(offset, max);
                } else {
                    offset = max / 2;
                }
                this.arrowObj.css('background-position', 10 + 'px');
            },

            // public
            fixPos: function () {
                if (this.settings.inited !== true) { return; }
                // get reference infomation
                var refInfo = getElementPosition(this.refObj.get(0));
                // position
                var pos = this.getPos(refInfo);
                this.arrowLeft(pos.arrowLeft);
                delete pos['arrowLeft'];
                this.el.css(pos);
            },

            // public
            show: function (ref) {
                if (this.settings.showed === true) { return; }
                if (ref) { this.refObj = ref; }
                this.initialize();
                // set position
                //this.fixPos();
                // show
                this.el.show();
                // register
                //$(document).bind('mouseup', this.settings._hide);
                //$(window).bind('resize', this.settings._reisze);
                this.settings.showed = true;
                // event
                if (this.settings.onShow) { this.settings.onShow.call(this); }
            },

            // public
            hide: function () {
                if (this.settings.inited !== true) { return; }
                // remove
                this.el.remove();
                // unregister
                //$(document).unbind('mouseup', this.settings._hide);
                //$(window).unbind('resize', this.settings._reisze);
                this.settings.showed = false;
                // event
                if (this.settings.onHide) { this.settings.onHide.call(this); }
            },

            // public
            setForm: function (form) {
                if (this.settings.inited !== true) {
                    this.settings.lazyFunc.push(function (cnt) {
                        return function () { this.setForm(cnt); };
                    } (form));
                    return;
                }
                $(this.el).attr("form", form);
            },

            // public
            setSelector: function (selector) {
                if (this.settings.inited !== true) {
                    this.settings.lazyFunc.push(function (cnt) {
                        return function () { this.setSelector(cnt); };
                    } (selector));
                    return;
                }
                $(this.el).attr("selector", selector);
            },

            // public
            setErrorMessage: function (errorMessage) {
                if (this.settings.inited !== true) {
                    this.settings.lazyFunc.push(function (cnt) {
                        return function () { this.setErrorMessage(cnt); };
                    } (errorMessage));
                    return;
                }
                $(this.el).html(errorMessage);
            }
        }
    });

    /*
    * jquery entrance
    */
    $.extend($.fn, {
        formValid: function (options) {

            // closure
            options = options || {};
            var timeout = 500;
            var current = null;

            // element cache container
            if (!options.renderTo && $('.form-valid-cache').length == 0)
                $('<div class="form-valid-cache"></div>').appendTo('body');

            // check has elements
            if (!$(this).length) {
                options.debug && window.console && console.warn('nothing selected');
                return;
            }

            // loop
            this.each(function () {

                // get cache
                //var valid = $.data(this, 'formValid');
                //if (valid) { return valid; }

                // get form
                var form, f = options.form;
                if (f != null) {
                    form = (typeof (f) === 'function') ? f.call(this) : f;
                }
                // get selector
                var selector, s = options.selector;
                if (s != null) {
                    selector = (typeof (s) === 'function') ? s.call(this) : s;
                }
                // get messages
                var errorMessage, em = options.errorMessage;
                if (em != null) {
                    errorMessage = (typeof (em) === 'function') ? em.call(this) : em;
                } else {
                    errorMessage = $(this).next().html();
                }

                // new instance
                valid = new $.formValid(options, $(this));

                if (form) {
                    valid.setForm(form);
                }
                if (selector) {
                    valid.setSelector(selector);
                }
                if (errorMessage) {
                    valid.setErrorMessage(errorMessage);
                }

                // set cache
                //$.data(this, 'formValid', valid);

                //Show
                valid.show();

                //Hide
                var trigger = (options.trigger || 'change').toLowerCase();
                if (trigger == 'change') {
                    $(this).change(function () {
                        //clearTimeout($(this).data('tid'));
                        valid.hide();
                    });
                }
                // ret
                return valid;
            });

            // ret
            return this;
        }
    });
})(jQuery);

jQuery.extend({
    formValidClear: function (target) {
        target = target;

        if (target == null || target == "") {
            $('body').find('.field-validation-error').remove();
        } else {
            target.find('.field-validation-error').remove();
        }
    }
});